/*********************************************************************************************************************
* MM32F527X-E9P Opensource Library 即（MM32F527X-E9P 开源库）是一个基于官方 SDK 接口的第三方开源库
* Copyright (c) 2022 SEEKFREE 逐飞科技
* 
* 本文件是 MM32F527X-E9P 开源库的一部分
* 
* MM32F527X-E9P 开源库 是免费软件
* 您可以根据自由软件基金会发布的 GPL（GNU General Public License，即 GNU通用公共许可证）的条款
* 即 GPL 的第3版（即 GPL3.0）或（您选择的）任何后来的版本，重新发布和/或修改它
* 
* 本开源库的发布是希望它能发挥作用，但并未对其作任何的保证
* 甚至没有隐含的适销性或适合特定用途的保证
* 更多细节请参见 GPL
* 
* 您应该在收到本开源库的同时收到一份 GPL 的副本
* 如果没有，请参阅<https://www.gnu.org/licenses/>
* 
* 额外注明：
* 本开源库使用 GPL3.0 开源许可证协议 以上许可申明为译文版本
* 许可申明英文版在 libraries/doc 文件夹下的 GPL3_permission_statement.txt 文件中
* 许可证副本在 libraries 文件夹下 即该文件夹下的 LICENSE 文件
* 欢迎各位使用并传播本程序 但修改内容时必须保留逐飞科技的版权声明（即本声明）
* 
* 文件名称          zf_device_spi_tfcard
* 公司名称          成都逐飞科技有限公司
* 版本信息          查看 libraries/doc 文件夹内 version 文件 版本说明
* 开发环境          MDK 5.37
* 适用平台          MM32F527X_E9P
* 店铺链接          https://seekfree.taobao.com/
* 
* 修改记录
* 日期              作者                备注
* 2022-08-10        Teternal            first version
********************************************************************************************************************/

#include "zf_common_headfile.h"

#include "zf_device_spi_tfcard.h"       

/* SD卡信息 */
tfcard_information_struct tfcard_information;

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     TF 卡取消选择 释放总线
// 参数说明     void
// 返回参数     void
// 使用示例     tfcard_dis_selected();
// 备注信息     内部调用
//-------------------------------------------------------------------------------------------------------------------
static void tfcard_dis_selected (void)
{
    uint8 data = 0xFF;
    TFCARD_CS(1);
    spi_transfer_8bit(TFCARD_SPI, &data, &data, 1);
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     等待 TF 卡准备
// 参数说明     void
// 返回参数     uint8           0 - TF 卡准备就绪 1 - 未响应
// 使用示例     tfcard_waitready();
// 备注信息     内部调用
//-------------------------------------------------------------------------------------------------------------------
static uint8 tfcard_waitready (void)
{
    uint16_t count = 0;
    uint8 data = 0xFF;

    do
    {
        data = 0xFF;
        spi_transfer_8bit(TFCARD_SPI, &data, &data, 1);
        if(0xFF == data)
        {
            break;
        }
        count ++;           
    }while(0xFFF0 > count); 
    data = (0xFFF0 > count) ? (0) : (1);

    return data;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     选中 TF 卡 等待 TF 卡准备
// 参数说明     void
// 返回参数     uint8           0 - TF 卡准备就绪 1 - 未响应
// 使用示例     tfcard_selected();
// 备注信息     内部调用
//-------------------------------------------------------------------------------------------------------------------
static uint8 tfcard_selected (void)
{
    uint8 data = 0xFF;

    TFCARD_CS(0);
    spi_transfer_8bit(TFCARD_SPI, &data, &data, 1);

    data = tfcard_waitready();
    if(data)
    {
        tfcard_dis_selected();
    }
    return data;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     等待 TF 卡回应
// 参数说明     response        需要等待的响应
// 返回参数     uint8           0 - 响应 1 - 未响应
// 使用示例     tfcard_get_response(0xFE);
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_get_response (uint8 response)
{
    uint16_t count = 0x1FFF;
    uint8 data = 0xFF;

    while((data != response) && count)
    {
        data = 0xFF;
        spi_transfer_8bit(TFCARD_SPI, &data, &data, 1);
        count --;     
    }

    return (count == 0) ? (TFCARD_MSD_RESPONSE_FAILURE) : (TFCARD_MSD_RESPONSE_NO_ERROR);
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     向 TF 卡发送命令
// 参数说明     cmd             命令
// 参数说明     arg             命令参数
// 参数说明     crc             CRC
// 返回参数     uint8           0 - 响应 1 - 未响应
// 使用示例     tfcard_get_response(0xFE);
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_send_cmd (uint8 cmd, uint32 arg, uint8 crc)
{
    uint8 return_state = 0xFF;
    uint8 count = 0xFF; 
    uint8 data[6] = {
        cmd | 0x40, (arg >> 24) & 0xFF,
        (arg >> 16) & 0xFF, (arg >> 8) & 0xFF,
        arg & 0xFF, crc};
    
    tfcard_dis_selected();
    if(!tfcard_selected())
    {
        spi_transfer_8bit(TFCARD_SPI, data, data, 6);
        if(TFCARD_CMD12 == cmd)
        {
            data[0] = 0xFF;
            spi_transfer_8bit(TFCARD_SPI, &data[0], &data[0], 1);
        }
        do
        {
            data[0] = 0xFF;
            spi_transfer_8bit(TFCARD_SPI, &data[0], &data[0], 1);
            count --;
        }while((data[0] & 0x80) && count);
        return_state = data[0];
    }
    
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     查询 TF 卡状态
// 参数说明     void
// 返回参数     uint8           TF 卡状态
// 使用示例     tfcard_get_state();
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_get_state (void)
{
    uint8 retval[2] = {0xFF, 0xFF};

    retval[0] = tfcard_send_cmd(TFCARD_CMD13, 0, 0xFF);
    spi_transfer_8bit(TFCARD_SPI, &retval[1], &retval[1], 1);
    tfcard_dis_selected();

    return !((0 == retval[0]) && (0 == retval[1]));
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     按扇区从 TF 卡读取数据
// 参数说明     buff            数据缓存区
// 参数说明     len             数据缓存区长度 每次最多 512
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_read_data(buff, 128);
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_read_data (uint8 *buff, uint32 len)
{
    uint8 return_state = 1;
    uint8 data[512];
    if(512 >= len)
    {
        memset(data, 0xFF, len);
        if(TFCARD_MSD_RESPONSE_NO_ERROR == tfcard_get_response(0xFE))
        {
            spi_transfer_8bit(TFCARD_SPI, data, buff, len);
//            for(uint16 i = 0; len > i; i ++)
//            {
//                spi_transfer_8bit(TFCARD_SPI, &data[i], &buff[i], 1);
//            }
            
            spi_transfer_8bit(TFCARD_SPI, data, data, 2);
            return_state = 0;
        }
    }
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     按扇区向 TF 卡发送数据
// 参数说明     buff            数据缓存区 1 个扇区固定为 512 字节
// 参数说明     cmd             指令
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_write_block(buff, 0xFE);
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_write_block (uint8 *buff, uint8 cmd)
{
    uint8   return_state = 1;
    uint8   data[512];
    uint16  count = 0;  
    memset(data, 0xFF, 512);

    if(!tfcard_waitready())
    {
        data[0] = cmd;
        spi_transfer_8bit(TFCARD_SPI, &data[0], &data[0], 1);
        data[0] = 0xFF;
        if(0xFD != cmd)                                                         // 不是结束指令
        {
            spi_transfer_8bit(TFCARD_SPI, buff, data, 512);
//            for(uint16 i = 0; 512 > i; i ++)
//            {
//                spi_transfer_8bit(TFCARD_SPI, &buff[i], &data[i], 1);
//            }
            
            memset(data, 0xFF, 3);
            spi_transfer_8bit(TFCARD_SPI, data, data, 3);
            
            return_state = 0;
            if(TFCARD_MSD_DATA_OK != (0x1F & data[2]))                          // 正常响应为 xxx00101
            {
                return_state = 2;                                               // 响应错误
            }
            if(tfcard_waitready())                                              // 延时等待SD卡内部数据写入完成 上面的操作只是把数据发送到SD卡控制器缓存中 还没有写入到SD卡闪存内
            {
                return_state = 1;                                               // 等待准备失效
            }
        }
    }

    return return_state;   // 写入成功
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     获取 TF 卡的 CID 信息
// 参数说明     void
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_get_cid();
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_get_cid (void)
{
    uint8 return_state = 1;
    memset(tfcard_information.tfcard_cid, 0xFF, 16);
    if(0x00 == tfcard_send_cmd(TFCARD_CMD10, 0, 0x39))                          // 发 CMD10 命令 读 CID
    {
        return_state = tfcard_read_data(tfcard_information.tfcard_cid, 16);     // 接收 16 个字节的数据  
    }

    tfcard_dis_selected();  // 取消片选
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     获取 TF 卡的 CSD 信息
// 参数说明     void
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_get_csd();
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_get_csd (void)
{
    uint8 return_state = 1;
    memset(tfcard_information.tfcard_csd, 0xFF, 16);
    if(0x00 == tfcard_send_cmd(TFCARD_CMD9, 0, 0xAF))                           // 发 CMD9 命令 读 CSD
    {
        return_state = tfcard_read_data(tfcard_information.tfcard_csd, 16);     // 接收 16 个字节的数据  
    }

    tfcard_dis_selected();
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     获取 TF 卡的容量信息
// 参数说明     void
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_get_capacity();
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_get_capacity (void)
{
    uint8 return_state = 1;
    if(!tfcard_get_csd())
    {
        if(0x40 == (tfcard_information.tfcard_csd[0] & 0xC0))                   // 判断 CSD V2
        {
            tfcard_information.tfcard_capacity_kbyte  = tfcard_information.tfcard_csd[9];
            tfcard_information.tfcard_capacity_kbyte += ((uint32)tfcard_information.tfcard_csd[8] << 8);
            tfcard_information.tfcard_capacity_kbyte += ((uint32)(tfcard_information.tfcard_csd[7] & 63) << 16);
            tfcard_information.tfcard_capacity_kbyte += 1;
            tfcard_information.tfcard_capacity_kbyte = tfcard_information.tfcard_capacity_kbyte << 9; 
        }
        else                                                                    // 判断 CSD V1
        {
            uint32 size = 0;
            uint32 mult = 0;
            size  = (tfcard_information.tfcard_csd[8] >> 6);
            size += ((uint16)tfcard_information.tfcard_csd[7] << 2);
            size += ((uint16)(tfcard_information.tfcard_csd[6] & 3) << 10);
            size += 1;
            mult  = (tfcard_information.tfcard_csd[5] & 0x0F);
            mult += ((tfcard_information.tfcard_csd[10] & 0x80) >> 7);
            mult += ((tfcard_information.tfcard_csd[9] & 0x03) << 1);
            mult += 2;
            tfcard_information.tfcard_capacity_kbyte = size << (mult - 10);
        }
        tfcard_information.tfcard_sector_max = tfcard_information.tfcard_capacity_kbyte * 2;
        return_state = 0;
    }
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     TF 卡进入空闲模式
// 参数说明     void
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_enter_idle_mode();
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_enter_idle_mode (void)
{
    uint8 data[16];
    
    TFCARD_CS(1);
    memset(data, 0xFF, 16);
    spi_transfer_8bit(TFCARD_SPI, data, data, 16);

    TFCARD_CS(0);
    data[1] = 0x20;
    do
    {
        data[0] = tfcard_send_cmd(TFCARD_CMD0, 0, 0x95);
        data[1] --;
    }while((0x01 != data[0]) && data[1]);
    
    return 0;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     TF 卡进入高速模式
// 参数说明     void
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_enter_high_speed_mode();
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_enter_high_speed_mode (void)
{
    uint8 return_state = 1;
    uint8_t temp[66];
    uint8_t count = 0xFF;

    spi_init(TFCARD_SPI, SPI_MODE0, TFCARD_SPI_INIT_SPEED, TFCARD_SCK_PIN, TFCARD_MOSI_PIN, TFCARD_MISO_PIN, SPI_CS_NULL);

    do
    {
        temp[0] = tfcard_send_cmd(TFCARD_CMD6, 0x00FFFFF1, 0x01);
    }while(temp[0] && count --);
    
    while(TFCARD_MSD_RESPONSE_NO_ERROR == tfcard_get_response(0xFE))
    {
        memset(temp, 0xFF, 66);
        spi_transfer_8bit(TFCARD_SPI, temp, temp, 66);
        
        if(0x01 != (temp[16] & 0x0F))                                           // 不支持
        {
            spi_init(TFCARD_SPI, SPI_MODE0, TFCARD_SPI_WORK_SPEED, TFCARD_SCK_PIN, TFCARD_MOSI_PIN, TFCARD_MISO_PIN, SPI_CS_NULL);
            break;
        }
        
        spi_init(TFCARD_SPI, SPI_MODE0, TFCARD_SPI_WORK_SPEED * 2, TFCARD_SCK_PIN, TFCARD_MOSI_PIN, TFCARD_MISO_PIN, SPI_CS_NULL);
        return_state = 0;
        break;
    }
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     TF 卡读扇区
// 参数说明     *buff           数据缓冲区
// 参数说明     sector          起始扇区
// 参数说明     cnt             扇区数
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_read_sector(buff, 0, 1);
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_read_sector (uint8 *buff, uint32 sector, uint32 cnt)
{
    uint8 return_state = 1;
    if(TFCARD_TYPE_SDHC != tfcard_information.tfcard_type)
    {
        sector *= 512;                                                          // 转换为字节地址
    }
    
    if(1 == cnt)
    {
        return_state = tfcard_send_cmd(TFCARD_CMD17, sector, 0x01);             // 读命令
        if(!return_state)                                                       // 指令发送成功
        {
            return_state = tfcard_read_data(buff, 512);                         // 接收 512 个字节      
        }
    }
    else
    {
        return_state = tfcard_send_cmd(TFCARD_CMD18, sector, 0x01);             // 连续读命令
        do
        {
            return_state = tfcard_read_data(buff, 512);                         // 接收 512 个字节    
            buff += 512;
            cnt --;
        }while(cnt && 0 == return_state);
        tfcard_send_cmd(TFCARD_CMD12, 0, 0x01);                                 // 发送停止命令
    }
    tfcard_dis_selected();                                                      // 取消片选
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     TF 卡写扇区
// 参数说明     *buff           数据缓冲区
// 参数说明     sector          起始扇区
// 参数说明     cnt             扇区数
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_write_sector(buff, 0, 1);
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_write_sector (uint8 *buff, uint32 sector, uint32 cnt)
{
    uint8 return_state = 1;
    if(TFCARD_TYPE_SDHC != tfcard_information.tfcard_type)
    {
        sector *= 512;                                                          // 转换为字节地址
    }
    if(1 == cnt)
    {
        return_state = tfcard_send_cmd(TFCARD_CMD24, sector, 0x01);             // 写命令
        if(0x00 == return_state)                                                // 指令发送成功
        {
            return_state = tfcard_write_block(buff, 0xFE);                      // 写 512 个字节
        }
//      else
//      {
//          tfcard_init();
//      }
    }
    else
    {
        if(TFCARD_TYPE_MMC != tfcard_information.tfcard_type)
        {
            tfcard_send_cmd(TFCARD_CMD55, 0, 0x01); 
            tfcard_send_cmd(TFCARD_CMD23, cnt, 0x01);
        }
        
        return_state = tfcard_send_cmd(TFCARD_CMD25, sector, 0x01);             // 连续写命令
        while(0 == return_state && cnt)
        {
            return_state = tfcard_write_block(buff, 0xFC);                      // 写 512 个字节
            buff += 512;
            cnt --;
        }
    }
    tfcard_dis_selected();                                                      // 取消片选
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     TF 卡类型和容量信息打印
// 参数说明     void
// 返回参数     void
// 使用示例     tfcard_information_printf();
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
void tfcard_information_printf (void)
{
    switch(tfcard_information.tfcard_type)
    {
        case TFCARD_TYPE_ERROR: printf("TF-Card Type:  Unknow, "); break;
        case TFCARD_TYPE_MMC:   printf("TF-Card Type:  SD_MMC, "); break;
        case TFCARD_TYPE_SDV1:  printf("TF-Card Type: SD_SDV1, "); break;
        case TFCARD_TYPE_SDV2:  printf("TF-Card Type: SD_SDV2, "); break;
        case TFCARD_TYPE_SDHC:  printf("TF-Card Type:   SD_HC, "); break;
        case TFCARD_TYPE_SDXC:  printf("TF-Card Type:   SD_XC, "); break;
    }
    printf("speed Mode: %s, ", tfcard_information.tfcard_speed ? "High Speed": "Default");
    printf("Sector Max: %d, ", tfcard_information.tfcard_sector_max);
    if(1024 < tfcard_information.tfcard_capacity_kbyte / 1024)
    {
        printf("Capacity: %4.2f GB.\r\n", (float)tfcard_information.tfcard_capacity_kbyte / 1024.0 / 1024.0);
    }
    else
    {
        printf("Capacity: %4.2f MB.\r\n", (float)tfcard_information.tfcard_capacity_kbyte / 1024.0);
    }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介     TF 卡初始化
// 参数说明     void
// 返回参数     uint8           0 - 成功 1 - 失败
// 使用示例     tfcard_init();
// 备注信息     
//-------------------------------------------------------------------------------------------------------------------
uint8 tfcard_init (void)
{
    tfcard_information.tfcard_type = TFCARD_TYPE_ERROR;
    uint16 count = 0x20;
    uint8 init_state = 0;
    uint8 return_state = 1;

    spi_init(TFCARD_SPI, SPI_MODE0, TFCARD_SPI_INIT_SPEED, TFCARD_SCK_PIN, TFCARD_MOSI_PIN, TFCARD_MISO_PIN, SPI_CS_NULL);
    gpio_init(TFCARD_CS_PIN, GPO, GPIO_HIGH, GPO_PUSH_PULL);

    uint8 data[16];
    memset(data, 0xFF, 16);
    spi_transfer_8bit(TFCARD_SPI, data, data, 16);

    TFCARD_CS(0);
    do
    {
        init_state = tfcard_send_cmd(TFCARD_CMD0, 0, 0x95);                     // 进入空闲状态
    }while((0x01 != init_state) && count--);

    if(0x01 == init_state)
    {
        if(0x01 == tfcard_send_cmd(TFCARD_CMD8, 0x1AA, 0x87))                   // V2.0
        {
            memset(data, 0xFF, 4);
            spi_transfer_8bit(TFCARD_SPI, data, data, 16);
            if(0x01 == data[2] && 0xAA == data[3])                              // 是否支持 2.7~3.6V
            {
                count = 0x1FFF;
                do                                                              // 等待退出空闲模式
                {
                    tfcard_send_cmd(TFCARD_CMD55, 0, 0x01);
                    init_state = tfcard_send_cmd(TFCARD_CMD41, 0x40000000, 0x01);
                }while(init_state && count--);
                
                if(count && 0 == tfcard_send_cmd(TFCARD_CMD58, 0, 0x01))        // 鉴别 2.0版本
                {
                    memset(data, 0xFF, 4);
                    spi_transfer_8bit(TFCARD_SPI, data, data, 16);
                    if(data[0] & 0x40)
                    {
                        tfcard_information.tfcard_type = TFCARD_TYPE_SDHC;
                        return_state = 0;
                    }
                    else 
                    {
                        tfcard_information.tfcard_type = TFCARD_TYPE_SDV2;
                        return_state = 0;
                    }
                }
            }
        }
        else                                                                    // V1.0 / MMC
        {
            tfcard_send_cmd(TFCARD_CMD55, 0, 0x01);
            init_state = tfcard_send_cmd(TFCARD_CMD41, 0, 0x01);
            if(1 >= init_state)
            {       
                tfcard_information.tfcard_type = TFCARD_TYPE_SDV1;
                return_state = 0;
                count = 0x1FFF;
                do                                                              // 等待退出空闲模式
                {
                    tfcard_send_cmd(TFCARD_CMD55, 0, 0x01);
                    init_state = tfcard_send_cmd(TFCARD_CMD41, 0, 0x01);
                }while(init_state && count--);
            }
            else                                                                // MMC 不支持 CMD55 + CMD41 识别
            {
                tfcard_information.tfcard_type = TFCARD_TYPE_MMC;               // MMC
                return_state = 0;
                count = 0x1FFF;
                do
                {
                    init_state = tfcard_send_cmd(TFCARD_CMD1, 0, 0x01);         // 发送 CMD1
                }while(init_state && count --);
            }
            
            if(0 == count || 0 != tfcard_send_cmd(TFCARD_CMD16, 512, 0x01))
            {
                tfcard_information.tfcard_type = TFCARD_TYPE_ERROR;             // 未识别
            }
        }
    }
    
    tfcard_dis_selected();
    spi_init(TFCARD_SPI, SPI_MODE0, TFCARD_SPI_WORK_SPEED, TFCARD_SCK_PIN, TFCARD_MOSI_PIN, TFCARD_MISO_PIN, SPI_CS_NULL);
    tfcard_information.tfcard_speed = (0 == tfcard_enter_high_speed_mode());
    if(!return_state)
    {
        return_state = tfcard_get_capacity();
        if( (1024 * 32 < tfcard_information.tfcard_capacity_kbyte / 1024) &&
            (TFCARD_TYPE_SDHC == tfcard_information.tfcard_type))
        {
            tfcard_information.tfcard_type = TFCARD_TYPE_SDXC;
        }
    }
    
    return return_state;
}
